/*
	generate_ipv4_mul2uni.c 
	by yingang.fu@borqs.com	

	Simple generate an IPv4 multicast to unicast packet using LORCON2
	library.
*/

#include <stdio.h>
#include <getopt.h>
#include <string.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <lorcon2/lorcon.h> // For LORCON 
#include <lorcon2/lorcon_packasm.h> // For metapack packet assembly
#include <lorcon2/lorcon_ieee80211.h>
#include "network_Utility.h"

void usage(char *argv[]) {
	printf("\t-s <Mac address> \tmac address of sender\n");
	printf("\t-d <Mac address> \tmac address of receiver\n");
	printf("\t-p <ip address> \tip address of sender\n");
	printf("\t-r <ip address> \tip address of receiver\n");
	printf("\t-b <bssid> \tBSSID\n");
	printf("\t-i <int> \tInterface\n");
	printf("\t-c <channel>\tChannel\n");
	printf("\t-t \tTo DS\n");
	printf("\nExample:\n");
	printf("\t%s -s 08:00:28:AA:BB:CC -d 08:00:28:AA:BB:DD -p 192.168.1.131 -r 224.0.0.252 -i wlan0 -c 1 -b 58:6D:8F:85:CF:56\n\n",argv[0]);
}
int main(int argc, char *argv[]) {

	char *interface = NULL;
	int c,k;
	uint8_t channel = 0;
	unsigned int count=0;
	char *s_input = NULL;
	char *d_input = NULL;
	char *si_input = NULL;
	char *di_input = NULL;
	char *bssid_input = NULL;
	uint16_t check_sum;
	in_addr_t addr_sender, addr_receiver;
	struct timeval tv;
	int toDS = 0;

	lorcon_driver_t *drvlist, *driver; // Needed to set up interface/context
	lorcon_t *context; // LORCON context

	lcpa_metapack_t *metapack; // metapack for LORCON packet assembly 
	lorcon_packet_t *txpack; // The raw packet to be sent

	// BSSID and source MAC address
	uint8_t bssid[6];
	uint8_t da[6];
	uint8_t sa[6];
	uint8_t si[4];
	uint8_t di[4];	
	uint8_t llc[8];
	uint8_t ipv4[] = {
		0x45, // Version & IP header length
		0x00, // Type of Service
		0x00, 0x32, // Total length
		0x99, 0x10, // Identification
		0x00, 0x00, // Flags & Fragment offset
		0x21, //TTL
		0x01, // Protocol
		0x00, 0x00, //Checksum
		0x00, 0x00, 0x00, 0x00, // Source ip address 192.168.0.151
		0x00, 0x00, 0x00, 0x00 // Destinatio IP address 224.0.0.252
	};
	uint8_t icmp[] = {
		0x08, // Type (Echo request)
		0x00, // code
		0x00, 0x00, // Checksum
		0x51, 0x7b, // Identifier
		0x00, 0x01, // Sequence number
		0xba, 0xdb, 0xee, 0xf0, 0x01, 0x02
	};


	printf ("%s - Simple generate an IPv4 ICMP Request packet\n", argv[0]);
	printf ("-----------------------------------------------------\n\n");

	/* 
	   This handles all of the command line arguments
	*/
	
	while ((c = getopt(argc, argv, "i:s:t:d:p:r:b:hc:")) != EOF) {
		switch (c) {
			case 'i': 
				interface = strdup(optarg);
				break;
			case 's': 
				s_input = strdup(optarg);
				break;
			case 'd': 
				d_input = strdup(optarg);
				break;
			case 'b': 
				bssid_input = strdup(optarg);
				break;
			case 'p': 
				si_input = strdup(optarg);
				break;
			case 'r': 
				di_input = strdup(optarg);
				break;
			case 'c':
				channel = atoi(optarg);
				break;
			case 't':
				toDS = atoi(optarg);
				break;
			case 'h':
				usage(argv);
				return 0;
				break;
			default:
				usage(argv);
				break;
			}
	}

	if ( interface == NULL || s_input == NULL || d_input == NULL ||
               si_input == NULL || di_input == NULL || bssid_input == NULL || channel == 0) {
		usage(argv);		
		return -1;
	}

	gettimeofday(&tv, NULL);
	srand(tv.tv_usec);
	// Start process parameters
	
	addr_sender = inet_addr(si_input);	
	addr_receiver = inet_addr(di_input);	

	
	si[0] = addr_sender;
	si[1] = addr_sender>>8;
	si[2] = addr_sender>>16;
	si[3] = addr_sender>>24;

	di[0] = addr_receiver;
	di[1] = addr_receiver>>8;
	di[2] = addr_receiver>>16;
	di[3] = addr_receiver>>24;

	sscanf(s_input, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &sa[0], &sa[1], &sa[2], &sa[3], &sa[4], &sa[5]);
	sscanf(d_input, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &da[0], &da[1], &da[2], &da[3], &da[4], &da[5]);
	sscanf(bssid_input, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &bssid[0], &bssid[1], &bssid[2], &bssid[3], &bssid[4], &bssid[5]);

	ipv4[8] = rand(); // Generate a random TTL
	ipv4[12] = si[0];
	ipv4[13] = si[1];
	ipv4[14] = si[2];
	ipv4[15] = si[3]; 

	ipv4[16] = di[0];
	ipv4[17] = di[1];
	ipv4[18] = di[2];
	ipv4[19] = di[3];

	check_sum = cksum(ipv4, 20);
	ipv4[10] = check_sum>>8;
	ipv4[11] = check_sum;

	check_sum = cksum(icmp, 14);
	icmp[2] = check_sum>>8;
	icmp[3] = check_sum;

	
	/*	
	 	The following is all of the standard interface, driver, and context setup
	*/

	// Automatically determine the driver of the interface
	
	if ( (driver = lorcon_auto_driver(interface)) == NULL) {
		printf("[!] Could not determine the driver for %s\n",interface);
		return -1;
	} else {
		printf("[+]\t Driver: %s\n",driver->name);
	}

	// Create LORCON context
        if ((context = lorcon_create(interface, driver)) == NULL) {
                printf("[!]\t Failed to create context");
               	return -1; 
        }

	// Create Monitor Mode Interface
	if (lorcon_open_injmon(context) < 0) {
		printf("[!]\t Could not create Monitor Mode interface!\n");
		return -1;
	} else {
		printf("[+]\t Monitor Mode VAP: %s\n",lorcon_get_vap(context));
		lorcon_free_driver_list(driver);
	}

	// Set the channel we'll be injecting on
	lorcon_set_channel(context, channel);
	printf("[+]\t Using channel: %d\n\n",channel);

	/* 
	   The following is the packet creation and sending code
	 */

	// Initialize the LORCON metapack	
	metapack = lcpa_init();

	//lcpf_80211headers(metapack, WLAN_FC_TYPE_DATA, WLAN_FC_SUBTYPE_QOSNULL, WLAN_FC_TODS, 0xff, bssid, sa, da, NULL, 0, 0);
	//lcpf_qosheaders(metapack, 0, 0, 0);
	if (toDS)
		lcpf_80211headers(metapack, WLAN_FC_TYPE_DATA, WLAN_FC_SUBTYPE_DATA, WLAN_FC_TODS, 0xff, bssid, sa, da, NULL, 0, rand());
	else
		lcpf_80211headers(metapack, WLAN_FC_TYPE_DATA, WLAN_FC_SUBTYPE_DATA, WLAN_FC_FROMDS, 0xff, da, bssid, sa, NULL, 0, rand());
	llc[0] = 0xaa;
	llc[1] = 0xaa;
	llc[2] = 0x03;
	llc[3] = 0x00;
	llc[4] = 0x00;
	llc[5] = 0x00;
	llc[6] = 0x08;
	llc[7] = 0x00;
	metapack = lcpa_append_copy(metapack, "LLC", 8, llc);

	//lcpf_qosheaders(metapack, 0, 0, 0);
	//metapack = lcpa_append_copy(metapack, "LLC", 3, llc);

	metapack = lcpa_append_copy(metapack, "ip", 20, ipv4);

	metapack = lcpa_append_copy(metapack, "icmp", 14, icmp);
	// Convert the LORCON metapack to a LORCON packet for sending
	txpack = (lorcon_packet_t *) lorcon_packet_from_lcpa(context, metapack);

	// Send and exit if error
	if ( lorcon_inject(context,txpack) < 0 ) 
		return -1;

	// Free the metapack
	lcpa_free(metapack);

	/* 
	   The following is all of the standard cleanup stuff
	 */

	// Close the interface
	lorcon_close(context);

	// Free the LORCON Context
	lorcon_free(context);	

	return 0;
}
